package universe;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Observable;

public class Board extends Observable {



	private Cell[][] matrix; // width, height
	private int width;
	private int height;
	private int currentIdNumber = 1;

	private int blockWidth = Utils.getBlockWidth();
	private int blockHeight = Utils.getBlockHeight();


	public Board (int pWidth,int pHeight){
		assert(pWidth>0 && pHeight>0);
		width = pWidth;
		height= pHeight;
		matrix = new Cell[width][height];
	}

	public Cell[][] getMatrix() { 
		return matrix; 
	}

	public int getNewestId(){
		return currentIdNumber++;
	}


	// Positionne un block dans la matrice
	// Note : Aucun test n'est fait ici

	public void pushBlock(Block pBlock,int x, int y){
		this.pushBlock(pBlock, x, y, 0);
	}

	public void pushBlock (Block pBlock,int x, int y, int id){
		int blockId = 0;
		if(id!=0){
			blockId = id;	
		}
		else{
			blockId = getNewestId();
		}

		int bHeight = pBlock.getHeight();
		int bWidth = pBlock.getWidth();
		for ( int j=0 ; j < bHeight; j++){ 
			for ( int i=0 ; i < bWidth; i++){

				if(pBlock.getCell(i, j)!= 0){
					if (x+i < width && y+j < height)
						matrix[x+i][y+j]= new Cell(pBlock.getCell(i, j),blockId);
				}
			}
		}

	}

	// Vérifie si la piece passée en paramètre (pBlock) peut être placée à l'endroit x, y 
	public boolean pushAllowed(Block pBlock,int x, int y){

		boolean bAllowed = true;

		if(x > width || y > height){
			bAllowed = false;
		}

		int bHeight = pBlock.getHeight();
		int bWidth = pBlock.getWidth();

		for ( int j=0 ; j < bHeight && bAllowed; j++){ 
			for ( int i=0 ; i < bWidth && bAllowed; i++){
				if(pBlock.getCell(i, j)!= 0){
					if (x+i >=0 && x+i < width && y+j < height){
						if(matrix[x+i][y+j] != null){
							// Il y a déjà une pièce...
							bAllowed = false;
						}
					}else{
						// On sort de la matrice...
						bAllowed = false;
					}						
				}
			}			
		}
		return bAllowed;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public String toString()
	{
		String retour="";

		Cell[][] matrix = this.getMatrix();
		for(int j=0; j< height;j++){
			for(int i=0; i< width;i++){
				if(matrix[i][j]!=null){
					retour = retour + String.valueOf(matrix[i][j].getId());
				}
				else{
					retour += '0';
				}
			}
			retour = retour + "\n";
		}
		return retour;
	}


	public int detectLowestCompletedLine(int from){

		for(int line=from; line>0; --line){

			int i = 0;
			while(i<width && matrix[i][line] != null){
				++i;
			}
			if(i==width){
				return line;
			}

		}
		return 0;
	}


	public void collapseLine(int line){
		for(int l=line; l>0; --l){
			for(int i=0; i<width; ++i){
				matrix[i][l]=matrix[i][l-1];
			}
		}
	}

	public int removeCompletedLines(){
		int controlStartLine = height-1;			


		int count = 0;
		boolean gFall = true;

		while(gFall){
			int line = detectLowestCompletedLine(controlStartLine);
			while(line>0 ){
				collapseLine(line);
				++count;
				line=detectLowestCompletedLine(line);
			}
			
			
			if(application.Utils.gravityActivated)
				gFall = checkGravity();
			else
				gFall = false;
			
			
			
		}
		return count;
	}

	private boolean checkGravity(){
		int currentId = 0;
		boolean fall = false;
		ArrayList<Integer> viewIds = new ArrayList<Integer>();


		int blockX =0;
		int blockY =0;

		for(int j=height-1; j>=0;j--){
			for(int i=0; i<width;i++){
				if(matrix[i][j] != null){ 
					int[][] block = new int[7][7];
					currentId = matrix[i][j].getId();
					if(!viewIds.contains(currentId)){
						viewIds.add(currentId);
						blockX = i;
						blockY = j;
						for(int m=j;m>j-Utils.getBlockHeight();m--){
							for(int k=i-Utils.getBlockWidth();k<i+Utils.getBlockWidth();k++){
								if(k<width && m<height && k>=0 && m>=0 && matrix[k][m] != null){
									if(matrix[k][m].getId() == currentId){
										if(k < blockX) blockX = k;
										if(m < blockY) blockY = m;
										block[Utils.getBlockWidth()-1+k-i][Utils.getBlockHeight()-1+m-j] = matrix[k][m].getColorNumber();
										matrix[k][m] = null;
									}
								}
							}
						}


						Block theblock = new Block(block);

						BlockHandler theBlockHandler = new BlockHandler(theblock, this);
						theBlockHandler.setX(blockX);
						theBlockHandler.setY(blockY);
						if(theBlockHandler.dropDown()){
							fall = true;
						}
						this.pushBlock(theBlockHandler.getB(), theBlockHandler.getX(), theBlockHandler.getY(), currentId);

					}
				}
			}
		}
		return fall;
	}


	public void gameOver(){
		try {
			ArrayList<Point> gameOverLocation = display.Utils.getGameOverLocation();

			for(int i = 0; i< Utils.getBoardWidth()/2; i++){
				for(int j=0; j<Utils.getBoardHeight(); j++){
					//Balayage Gauche-droite
					Point tmp = new Point(i,j);
					if(gameOverLocation.contains(tmp)){
						matrix[i][j] = new Cell(display.Utils.colorGameOver,0);
					}
					else{
						matrix[i][j] = new Cell(display.Utils.colorGameOverBackground,0);

					}


					Thread.sleep(1);



					//Balayage droite-gauche
					Point tmp2 = new Point(Utils.getBoardWidth()-i-1,j);
					if(gameOverLocation.contains(tmp2)){
						matrix[Utils.getBoardWidth()-i-1][j] = new Cell(display.Utils.colorGameOver,0);
					}
					else{
						matrix[Utils.getBoardWidth()-i-1][j] = new Cell(display.Utils.colorGameOverBackground,0);

					}
				}
				this.setChanged();
				this.notifyObservers();

				Thread.sleep(250);

			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}




	public void clearBoard() {
		int maxi = this.getWidth();
		int maxj = this.getHeight();

		for(int j = 0; j < maxj; ++j){
			for(int i = 1; i < maxi; ++i){
				matrix[i][i] = null;
			}
		}
	}
}
